#include "CEnemyGenerator.h"
#include "GamePlayScene.h"
//#include "EnemyWave.h"
//#include "KeyProb.h"
//#include "KeyProbComparer.h"
#include "CRandom.h"

USING_NS_CC;

static int compareLessSize(const void* p1, const void* p2)
{
	CKeyProb<CEnemyParams::EEnemySize>* key1 = (CKeyProb<CEnemyParams::EEnemySize>*)p1;
	CKeyProb<CEnemyParams::EEnemySize>* key2 = (CKeyProb<CEnemyParams::EEnemySize>*)p2;
    return (key2->_prob - key1->_prob);
}

static int compareLessType(const void* p1, const void* p2)
{
	CKeyProb<CEnemyParams::EEnemyType>* key1 = (CKeyProb<CEnemyParams::EEnemyType>*)p1;
	CKeyProb<CEnemyParams::EEnemyType>* key2 = (CKeyProb<CEnemyParams::EEnemyType>*)p2;
    return (key2->_prob - key1->_prob);
}

CEnemyGenerator::CEnemyGenerator() :
	_pSpriteSheet(NULL),
	_pEnemies(NULL),
	_difficulty(0.0f),
	_currentWave(),
	_pWorld(NULL)
{
}

/**
 * Setup the enemy generator.
 *
 * @param pEnemies This parameter is created and released by the MainLayer.
 * @param pSpriteSheet This parameter is created and released by the MainLayer.
 */
void
	CEnemyGenerator::init(CCArray* pEnemies, CCSpriteBatchNode* pSpriteSheet, b2World* pWorld)
{
	_pEnemies = pEnemies;
	_pSpriteSheet = pSpriteSheet;
	_pWorld = pWorld;
	_currentWave.init();
	
	// Adding the EnemyGenerator as a child of the currently running scene enables scheduling.
	CCDirector::sharedDirector()->getRunningScene()->getChildByTag(GamePlayScene::mainLayerTag)->addChild(this);
}

/**
 * Generates a new enemy for the current enemy wave.
 */
void
CEnemyGenerator::generate(float dt)
{
	CRandom rand;
	int randomNumber = rand.NextInt(100);
	bool generateEnemy = randomNumber > 50;
	if(generateEnemy)
	{
		generateNewEnemy();
	}

	if(_currentWave.__enemyCount <= 0 )
	{
		// Prepare the next wave of enemies.
		float nextWaveDelay = 3.0f ;
		unschedule(schedule_selector(CEnemyGenerator::generate));
		_currentWave.__pEnemySizeProb->removeAllObjects();
		_currentWave.__pEnemyTypeProb->removeAllObjects();
		this->runAction(CCSequence::actions(CCDelayTime::actionWithDuration(nextWaveDelay),
						CCCallFunc::actionWithTarget(this, callfunc_selector(CEnemyGenerator::startNextWave)),
						NULL));
	}
}

/**
 * Generate a new enemy based on randomly selected type and size.
 */
void
CEnemyGenerator::generateNewEnemy()
{
	CEnemyParams enemyParams;
	enemyParams._type = getEnemyType();
	enemyParams._size = getEnemySize();
	switch(enemyParams._size)
	{
	case CEnemyParams::size1:
		enemyParams._health = 100 + _difficulty;
		enemyParams._speed = 120;
		break;
	case CEnemyParams::size2:
		enemyParams._health = 200 + _difficulty;
		enemyParams._speed = 75;
		break;
	case CEnemyParams::size3:
		enemyParams._health = 400 + _difficulty;
		enemyParams._speed = 35;
		break;
	case CEnemyParams::size4:
		enemyParams._health = 1000 + _difficulty;
		enemyParams._speed = 15;
		break;
	default:
		break;
	}

	CRandom rand;
	enemyParams._startingPosition = rand.NextInt(480);

	CEnemy* enemy = createEnemy(enemyParams);

	_pEnemies->addObject(enemy);
	enemy->start();
	_currentWave.__enemyCount--;
}

/**
 * Creates a new enemy based on type provided in enemyParams.
 */
CEnemy*
CEnemyGenerator::createEnemy(CEnemyParams enemyParams)
{
	//nhandt create enemy
	CEnemy* enemy = NULL;
	switch(enemyParams._type)
	{
	case CEnemyParams::type1:
		enemy = new CEnemyType1(enemyParams);
		break;
	case CEnemyParams::type2:
		enemy = new CEnemyType2(enemyParams);
		break;
	case CEnemyParams::type3:
		enemy = new CEnemyType3(enemyParams);
		break;
	default:
		break;
	}

	enemy->init(_pSpriteSheet, _pWorld);
	enemy->createBoxBodyForSize(enemyParams._size);

	return enemy;
}

/**
 * Select the next enemy size based on the current wave probability values.
 */
CEnemyParams::EEnemySize
CEnemyGenerator::getEnemySize()
{
	CRandom rand;
	int randomNumber = rand.NextInt(100);
	CEnemyParams::EEnemySize enemySize = (CEnemyParams::EEnemySize) (randomNumber % CEnemyParams::sizeCount);
	

	// Get the current wave probability values.
	CKeyProb<CEnemyParams::EEnemySize>* firstSize = (CKeyProb<CEnemyParams::EEnemySize>*)(_currentWave.__pEnemySizeProb->objectAtIndex(3));
	float firstSizeProb = 100.0f - firstSize->_prob;
	CKeyProb<CEnemyParams::EEnemySize>* secondSize = (CKeyProb<CEnemyParams::EEnemySize>*)(_currentWave.__pEnemySizeProb->objectAtIndex(2));
	float secondSizeProb = firstSizeProb - secondSize->_prob;
	CKeyProb<CEnemyParams::EEnemySize>* thirdSize = (CKeyProb<CEnemyParams::EEnemySize>*)(_currentWave.__pEnemySizeProb->objectAtIndex(1));
	float thirdSizeProb = secondSizeProb - thirdSize->_prob;
	CKeyProb<CEnemyParams::EEnemySize>* fourthSize = (CKeyProb<CEnemyParams::EEnemySize>*)(_currentWave.__pEnemySizeProb->objectAtIndex(0));

	// Select the size.
	if(randomNumber > firstSizeProb)
	{
		enemySize = firstSize->_type;
	}
	else if (randomNumber > secondSizeProb)
	{
		enemySize = secondSize->_type;
	}
	else if (randomNumber > thirdSizeProb)
	{
		enemySize = thirdSize->_type;
	}
	else
	{
		enemySize = fourthSize->_type;
	}
	return enemySize;
}

/**
 * Select the next enemy type based on the current wave probability values.
 */
CEnemyParams::EEnemyType
CEnemyGenerator::getEnemyType()
{
	CRandom rand;
	int randomNumber = rand.NextInt(100);
	CEnemyParams::EEnemyType enemyType = (CEnemyParams::EEnemyType)(randomNumber % CEnemyParams::typeCount);

	// Get the current wave probability values.
	CKeyProb<CEnemyParams::EEnemyType>* firstType = (CKeyProb<CEnemyParams::EEnemyType>*)(_currentWave.__pEnemyTypeProb->objectAtIndex(2));
	float firstTypeProb = 100.0f - firstType->_prob;
	CKeyProb<CEnemyParams::EEnemyType>* secondType = (CKeyProb<CEnemyParams::EEnemyType>*)(_currentWave.__pEnemyTypeProb->objectAtIndex(1));
	float secondTypeProb = firstTypeProb - secondType->_prob;
	CKeyProb<CEnemyParams::EEnemyType>* thirdType = (CKeyProb<CEnemyParams::EEnemyType>*)(_currentWave.__pEnemyTypeProb->objectAtIndex(0));

	// Select the type.
	if(randomNumber > firstTypeProb)
	{
		enemyType = firstType->_type;
	}
	else if (randomNumber > secondTypeProb)
	{
		enemyType = secondType->_type;
	}
	else
	{
		enemyType = thirdType->_type;
	}

	return enemyType;
}

void
CEnemyGenerator::startNextWave()
{
	_difficulty++;
	_currentWave.__enemyCount = 5 + _difficulty;

	setSizeProbability();
	setTypeProbability();

	schedule(schedule_selector(CEnemyGenerator::generate), 0.5f);
}

/**
 * Set the size probability values for the next enemy wave.
 */
void
CEnemyGenerator::setSizeProbability()
{
	float size1Prob = 100 - _difficulty * 6;

	float size2Prob = (100 - size1Prob) / 7 * 4;
	size2Prob = size1Prob < 0 ? size2Prob + size1Prob : size2Prob;

	float size3Prob = (100 - size1Prob) / 7 * 2;
	size3Prob = size2Prob < 0 ? size3Prob + size2Prob : size3Prob;

	float size4Prob = (100 - size1Prob) / 7;
	size4Prob = size3Prob < 0 ? size4Prob + size3Prob : size4Prob;

	size1Prob = size1Prob < 0 ? 0 : size1Prob;
	size2Prob = size2Prob < 0 ? 0 : size2Prob;

	CKeyProb<CEnemyParams::EEnemySize>* size1 = new CKeyProb<CEnemyParams::EEnemySize>();
	size1->_prob = size1Prob;
	size1->_type = CEnemyParams::size1;

	CKeyProb<CEnemyParams::EEnemySize>* size2 = new CKeyProb<CEnemyParams::EEnemySize>();
	size2->_prob = size2Prob;
	size2->_type = CEnemyParams::size2;

	CKeyProb<CEnemyParams::EEnemySize>* size3 = new CKeyProb<CEnemyParams::EEnemySize>();
	size3->_prob = size3Prob;
	size3->_type = CEnemyParams::size3;

	CKeyProb<CEnemyParams::EEnemySize>* size4 = new CKeyProb<CEnemyParams::EEnemySize>();
	size4->_prob = size4Prob;
	size4->_type = CEnemyParams::size4;

	_currentWave.__pEnemySizeProb->removeAllObjects();

	_currentWave.__pEnemySizeProb->addObject(size1);
	_currentWave.__pEnemySizeProb->addObject(size2);
	_currentWave.__pEnemySizeProb->addObject(size3);
	_currentWave.__pEnemySizeProb->addObject(size4);

	qsort(_currentWave.__pEnemySizeProb->data->arr, _currentWave.__pEnemySizeProb->data->num, sizeof(_currentWave.__pEnemySizeProb->data->arr[0]), compareLessSize);
}

/**
 * Set the type probability values for the next enemy wave.
 */
void
CEnemyGenerator::setTypeProbability()
{
	float type1Prob = 100 - _difficulty * 8;

	float type2Prob = (100 - type1Prob) / 3 * 2;
	type2Prob = type1Prob < 0 ? type2Prob + type1Prob : type2Prob;

	float type3Prob = (100 - type1Prob) / 3;
	type3Prob = type2Prob < 0 ? type3Prob + type2Prob : type3Prob;

	type1Prob = type1Prob < 0 ? 0 : type1Prob;

	if(type2Prob < 0)
	{
		_difficulty = 0;
	}

	CKeyProb<CEnemyParams::EEnemyType>* type1 = new CKeyProb<CEnemyParams::EEnemyType>();
	type1->_prob = type1Prob;
	type1->_type = CEnemyParams::type1;

	CKeyProb<CEnemyParams::EEnemyType>* type2 = new CKeyProb<CEnemyParams::EEnemyType>();
	type2->_prob = type2Prob;
	type2->_type = CEnemyParams::type2;

	CKeyProb<CEnemyParams::EEnemyType>* type3 = new CKeyProb<CEnemyParams::EEnemyType>();
	type3->_prob = type3Prob;
	type3->_type = CEnemyParams::type3;

	_currentWave.__pEnemyTypeProb->removeAllObjects();

	_currentWave.__pEnemyTypeProb->addObject(type1);
	_currentWave.__pEnemyTypeProb->addObject(type2);
	_currentWave.__pEnemyTypeProb->addObject(type3);

	
	qsort(_currentWave.__pEnemyTypeProb->data->arr, _currentWave.__pEnemyTypeProb->data->num, sizeof(_currentWave.__pEnemySizeProb->data->arr[0]), compareLessType);
}
